package commons.test;

import static commons.Preconditions.checkNotNull;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;

import network.communication.ConnectionFactory;
import network.communication.MessageHandler;
import network.communication.ServerProtocol;
import network.internal.communication.DefaultConnectionFactory;
import network.internal.communication.DefaultServerProtocol;
import network.internal.communication.DefaultServerSocketFactory;
import network.internal.communication.DefaultSocketFactory;
import network.internal.communication.ServerHandler;
import network.internal.daemon.ConnectionHandlerDaemon;
import network.internal.io.DefaultInputStreamFactory;
import network.internal.io.DefaultOutputStreamFactory;
import network.io.InputStreamFactory;
import network.io.OutputStreamFactory;
import server.AttributesStorer;
import server.StorageDevice;
import server.StorageUnitID;
import server.StorageUnitResolver;
import server.internal.DefaultAttributesStorer;
import server.internal.DefaultStorageDevice;
import server.internal.Namespace;
import client.MiniFileSystemClient;

import commons.FileAttributes;
import commons.exception.AlreadyRunningException;
import commons.exception.NotRunningException;

public class FileSystemPseudoDistributed {

	private final String											serverHostName					= "localhost";
	private final int												serverPort						= 5000;
	private final SocketFactory										socketFactory;
	private final ServerSocketFactory								serverSocketFactory;
	private final ConnectionFactory									connectionFactory;
	private final InputStreamFactory<Socket, ObjectInputStream>		inputStreamFactory;
	private final OutputStreamFactory<Socket, ObjectOutputStream>	outputStreamFactory;
	private final int												transferMaxBufferSize			= 1024;
	private final MessageHandler									serverHandler;
	private final ConnectionHandlerDaemon							connectionHandler;
	private final int												connectionHandlerThreadPoolSize	= 4;
	private final String											storageDeviceDirectoryPath;

	public FileSystemPseudoDistributed(final String storageDeviceDirectoryPath)
			throws IOException {

		checkNotNull("storageDeviceDirectoryPath must not be null.",
				storageDeviceDirectoryPath);

		this.storageDeviceDirectoryPath = storageDeviceDirectoryPath;

		// create factories
		socketFactory = new DefaultSocketFactory();
		serverSocketFactory = new DefaultServerSocketFactory();
		inputStreamFactory = new DefaultInputStreamFactory();
		outputStreamFactory = new DefaultOutputStreamFactory();
		connectionFactory = new DefaultConnectionFactory(inputStreamFactory,
				outputStreamFactory, transferMaxBufferSize);

		// create server components
		final StorageUnitResolver resolver = new Namespace(
				new HashMap<String, StorageUnitID>());
		final StorageDevice storageDevice = new DefaultStorageDevice(
				this.storageDeviceDirectoryPath);
		final AttributesStorer attributesStorer = new DefaultAttributesStorer(
				new HashMap<String, FileAttributes>());

		// create services
		serverHandler = new ServerHandler(resolver, storageDevice,
				attributesStorer);
		connectionHandler = new ConnectionHandlerDaemon(serverPort,
				serverHandler, serverSocketFactory, connectionFactory,
				connectionHandlerThreadPoolSize);
	}

	public MiniFileSystemClient getClient() {
		return null;
	}

	public ServerProtocol getProtocolToServer() throws UnknownHostException,
			IOException {
		return new DefaultServerProtocol(serverHostName, serverPort,
				socketFactory, connectionFactory);
	}

	public void startServer() throws AlreadyRunningException {
		connectionHandler.start();
	}

	public void stopServer() throws NotRunningException, IOException {
		connectionHandler.stop();
	}
}
